// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//
#include <pollux/expression/register_special_form.h>
#include <pollux/functions/registerer.h>
#include <pollux/functions/lib/is_null.h>
#include <pollux/functions/prestosql/cardinality.h>
#include <pollux/functions/prestosql/fail.h>
#include <pollux/functions/prestosql/greatest_least.h>
#include <pollux/functions/prestosql/in_predicate.h>
#include <pollux/functions/prestosql/reduce.h>
#include <pollux/functions/prestosql/types/ip_address_type.h>
#include <pollux/functions/prestosql/types/timestamp_with_time_zone_type.h>

namespace kumo::pollux::functions {
    namespace {
        void registerFailFunction(const std::vector<std::string> &names) {
            register_function<FailFunction, UnknownValue, Varchar>(names);
            register_function<FailFunction, UnknownValue, int32_t, Varchar>(names);
            register_function<FailFromJsonFunction, UnknownValue, Json>(names);
            register_function<FailFromJsonFunction, UnknownValue, int32_t, Json>(names);
        }

        template<typename T>
        void registerGreatestLeastFunction(const std::string &prefix) {
            register_function<ParameterBinder<GreatestFunction, T>, T, T, Variadic<T> >(
                {prefix + "greatest"});

            register_function<ParameterBinder<LeastFunction, T>, T, T, Variadic<T> >(
                {prefix + "least"});
        }

        void registerAllGreatestLeastFunctions(const std::string &prefix) {
            registerGreatestLeastFunction<bool>(prefix);
            registerGreatestLeastFunction<int8_t>(prefix);
            registerGreatestLeastFunction<int16_t>(prefix);
            registerGreatestLeastFunction<int32_t>(prefix);
            registerGreatestLeastFunction<int64_t>(prefix);
            registerGreatestLeastFunction<float>(prefix);
            registerGreatestLeastFunction<double>(prefix);
            registerGreatestLeastFunction<Varchar>(prefix);
            registerGreatestLeastFunction<LongDecimal<P1, S1> >(prefix);
            registerGreatestLeastFunction<ShortDecimal<P1, S1> >(prefix);
            registerGreatestLeastFunction<Date>(prefix);
            registerGreatestLeastFunction<Timestamp>(prefix);
            registerGreatestLeastFunction<TimestampWithTimezone>(prefix);
            registerGreatestLeastFunction<IPAddress>(prefix);
        }
    } // namespace

    extern void registerSubscriptFunction(
        const std::string &name,
        bool enableCaching);

    extern void registerElementAtFunction(
        const std::string &name,
        bool enableCaching);

    // Special form functions don't have any prefix.
    void registerAllSpecialFormGeneralFunctions() {
        exec::registerFunctionCallToSpecialForms();
        POLLUX_REGISTER_VECTOR_FUNCTION(udf_in, "in");
        register_function<
            GenericInPredicateFunction,
            bool,
            Generic<T1>,
            Variadic<Generic<T1> > >({"in"});
        POLLUX_REGISTER_VECTOR_FUNCTION(udf_concat_row, "row_constructor");
        registerIsNullFunction("is_null");
    }

    void registerGeneralFunctions(const std::string &prefix) {
        registerSubscriptFunction(prefix + "subscript", true);
        registerElementAtFunction(prefix + "element_at", true);

        POLLUX_REGISTER_VECTOR_FUNCTION(udf_transform, prefix + "transform");
        POLLUX_REGISTER_VECTOR_FUNCTION(udf_reduce, prefix + "reduce");
        registerReduceRewrites(prefix);
        POLLUX_REGISTER_VECTOR_FUNCTION(udf_array_filter, prefix + "filter");
        POLLUX_REGISTER_VECTOR_FUNCTION(udf_typeof, prefix + "typeof");

        registerAllGreatestLeastFunctions(prefix);

        register_function<CardinalityFunction, int64_t, Array<Generic<T1> > >(
            {prefix + "cardinality"});
        register_function<CardinalityFunction, int64_t, Map<Generic<T1>, Generic<T2> > >(
            {prefix + "cardinality"});

        registerFailFunction({prefix + "fail"});

        registerAllSpecialFormGeneralFunctions();
    }
} // namespace kumo::pollux::functions
